/*
 * Created on October 03, 2012 3:31 PM
 * 
 */
package sembr.dao.writer;

import java.io.File;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import sembr.entities.*;

/**
 * 
 * @author adriano
 */
public class XmlFileWriter 
{
    private DocumentBuilderFactory docFactory;
    private DocumentBuilder docBuilder;
    private String path;
    
    public XmlFileWriter(String path) 
    {
        this.path = path;
        docFactory = DocumentBuilderFactory.newInstance();
        
        try 
        {
            docBuilder = docFactory.newDocumentBuilder();
        } 
        catch (ParserConfigurationException ex) 
        {
            Logger.getLogger(XmlFileWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void save(EntityBase entity)
    {   
        // write the content into xml file
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
                
        if (entity.getClass() == Source.class)
        {
            try 
            {     
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource domSource = new DOMSource(populateXmlSource((Source)entity));
                StreamResult result = new StreamResult(new File(this.path + "/pre_processing/source.xml"));

                transformer.transform(domSource, result);

                System.out.println(" XML File of Source is saved.");
            } 
            
            catch (ParserConfigurationException | TransformerException ex) 
            {
                Logger.getLogger(XmlFileWriter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        else if (entity.getClass() == ComputacionalDomain.class)
        {            
            try 
            {               
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource domSource = new DOMSource(populateXmlComputacionalDomain((ComputacionalDomain)entity));
                StreamResult result = new StreamResult(new File(this.path + "/pre_processing/computacionalDomain.xml"));

                transformer.transform(domSource, result);

                System.out.println("XML file of Domain is saved.");
            } 
            
            catch (ParserConfigurationException | TransformerException ex) 
            {
                Logger.getLogger(XmlFileWriter.class.getName()).log(Level.SEVERE, null, ex);
            }            
        }
        
        else if (entity.getClass() == SimulationSetup.class)
        {
            try 
            {  
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource domSource = new DOMSource(populateXmlSetup((SimulationSetup)entity));
                StreamResult result = new StreamResult(new File(this.path + "/pre_processing/setup.xml"));

                transformer.transform(domSource, result);

                System.out.println("XML file of Simulation's setup is saved.");
            } 
            
            catch (ParserConfigurationException | TransformerException ex) 
            {
                Logger.getLogger(XmlFileWriter.class.getName()).log(Level.SEVERE, null, ex);
            }             
        }
        else
        {
            try 
            {  
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource domSource = new DOMSource(populateXmlProjectSettings((ProjectSettings)entity));
                StreamResult result = new StreamResult(new File(this.path + "/project_settings.xml"));

                transformer.transform(domSource, result);

                // save a backup file into meep directory
                // TODO: deploy, uncomment code below
                result = new StreamResult(new File("../sembr.meep/project_settings/project_settings.xml"));
                //result = new StreamResult(new File("/sembr.meep/project_settings/project_settings.xml"));
                
                transformer.transform(domSource, result);
                
                System.out.println("XML file of project settings is saved.");
            } 
            
            catch (ParserConfigurationException | TransformerException ex) 
            {
                Logger.getLogger(XmlFileWriter.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }     
    
    private Document populateXmlSetup(SimulationSetup setup) throws ParserConfigurationException 
    {
        // setup root element
        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("setup");
        rootElement.setAttribute("dimension", String.valueOf(setup.getDimension()));
        doc.appendChild(rootElement);
        
        Map<String, Double> dieletrics = setup.getDieletrics();
        Set<String> keySet = dieletrics.keySet();        
        Iterator<String> iterator = keySet.iterator();
        
        Element dieletricsElement = doc.createElement("dieletrics");
        rootElement.appendChild(dieletricsElement);
        
        while (iterator.hasNext())
        {
            String key = iterator.next();
            Element materialElement = doc.createElement(key);
            materialElement.appendChild(doc.createTextNode(String.valueOf(dieletrics.get(key))));
            dieletricsElement.appendChild(materialElement); 
        }       
        
        // computacional domain properties
        ComputacionalDomain domain = setup.getDomain();
        // domain root element
        Element rootDomainElement = doc.createElement("domain");
        rootElement.appendChild(rootDomainElement);
        
        // initial x coordinate element
        Element iCoordX = doc.createElement("xi");
        iCoordX.appendChild(doc.createTextNode(String.valueOf(domain.getCoordXInitial())));
        rootDomainElement.appendChild(iCoordX);
        
        // final x coordinate element
        Element fCoordX = doc.createElement("xf");
        fCoordX.appendChild(doc.createTextNode(String.valueOf(domain.getCoordXFinal())));
        rootDomainElement.appendChild(fCoordX);
        
        // initial y coordinate element
        Element iCoordY = doc.createElement("yi");
        iCoordY.appendChild(doc.createTextNode(String.valueOf(domain.getCoordYInitial())));
        rootDomainElement.appendChild(iCoordY);
        
        // final y coordinate element
        Element fCoordY = doc.createElement("yf");
        fCoordY.appendChild(doc.createTextNode(String.valueOf(domain.getCoordYFinal())));
        rootDomainElement.appendChild(fCoordY);
        
        // initial z coordinate element
        Element iCoordZ = doc.createElement("zi");
        iCoordZ.appendChild(doc.createTextNode(String.valueOf(domain.getCoordZInitial())));
        rootDomainElement.appendChild(iCoordZ);
        
        // final z coordinate element
        Element fCoordZ = doc.createElement("zf");
        fCoordZ.appendChild(doc.createTextNode(String.valueOf(domain.getCoordZFinal())));
        rootDomainElement.appendChild(fCoordZ);
        
        // x's extension element
        Element xExtension = doc.createElement("x_extension");
        xExtension.appendChild(doc.createTextNode(String.valueOf(domain.getXExtension())));
        rootDomainElement.appendChild(xExtension);
        
        // y's extension element
        Element yExtension = doc.createElement("y_extension");
        yExtension.appendChild(doc.createTextNode(String.valueOf(domain.getYExtension())));
        rootDomainElement.appendChild(yExtension);
        
        // z's extension element
        Element zExtension = doc.createElement("z_extension");
        zExtension.appendChild(doc.createTextNode(String.valueOf(domain.getZExtension())));
        rootDomainElement.appendChild(zExtension);
               
        // pml thickness element
        Element pmlTickness = doc.createElement("pml_thickness");
        pmlTickness.appendChild(doc.createTextNode(String.valueOf(domain.getPmlThickness())));
        rootDomainElement.appendChild(pmlTickness);       
         
        // resolution element
        Element resolution = doc.createElement("resolution");
        resolution.appendChild(doc.createTextNode(String.valueOf(domain.getResolution())));
        rootDomainElement.appendChild(resolution);
        
        // source properties
        Source source = setup.getSource();
        // source root element
        Element rootSourceElement = doc.createElement("source");
        rootElement.appendChild(rootSourceElement);
        
        // source type element
        Element type = doc.createElement("type");
        type.appendChild(doc.createTextNode(String.valueOf(source.getType())));
        rootSourceElement.appendChild(type);

        // frequency element
        Element frequency = doc.createElement("frequency");
        frequency.appendChild(doc.createTextNode(String.valueOf(source.getFrequency())));
        rootSourceElement.appendChild(frequency);

        // frequency width element
        Element width = doc.createElement("width");
        width.appendChild(doc.createTextNode(String.valueOf(source.getWidth())));
        rootSourceElement.appendChild(width);

        // start time element
        Element startTime = doc.createElement("start_time");
        startTime.appendChild(doc.createTextNode(String.valueOf(source.getStartTime())));
        rootSourceElement.appendChild(startTime);

        // end time element
        Element endTime = doc.createElement("end_time");
        endTime.appendChild(doc.createTextNode(String.valueOf(source.getEndTime())));
        rootSourceElement.appendChild(endTime);
        
        // setup's source position
        Element sourcePosition = doc.createElement("source_position");
        rootSourceElement.appendChild(sourcePosition);      
        
        // setup's x source postion
        Element sourceXPosition = doc.createElement("x");
        sourceXPosition.appendChild(doc.createTextNode(String.valueOf(source.getPosition()[0])));
        sourcePosition.appendChild(sourceXPosition);
        
        // setup's x source postion
        Element sourceYPosition = doc.createElement("y");
        sourceYPosition.appendChild(doc.createTextNode(String.valueOf(source.getPosition()[1])));
        sourcePosition.appendChild(sourceYPosition);
        
        // setup's x source postion
        Element sourceZPosition = doc.createElement("z");
        sourceZPosition.appendChild(doc.createTextNode(String.valueOf(source.getPosition()[2])));
        sourcePosition.appendChild(sourceZPosition);
        
        // field properties
        Field field = setup.getField();
        // field root element
        Element rootFieldElement = doc.createElement("field");
        rootElement.appendChild(rootFieldElement);
        
        // field type element
        Element fieldType = doc.createElement("type");
        fieldType.appendChild(doc.createTextNode(field.getFieldType()));
        rootFieldElement.appendChild(fieldType);
        
        Element fieldDirection = doc.createElement("direction");
        fieldDirection.appendChild(doc.createTextNode(field.getDirection()));
        rootFieldElement.appendChild(fieldDirection);
                
        // field initial x position
        Element fieldXiPosition = doc.createElement("xi");
        fieldXiPosition.appendChild(doc.createTextNode(String.valueOf(field.getInitialPosition()[0])));
        rootFieldElement.appendChild(fieldXiPosition);
                
        // field final x position
        Element fieldXfPosition = doc.createElement("xf");
        fieldXfPosition.appendChild(doc.createTextNode(String.valueOf(field.getFinalPosition()[0])));
        rootFieldElement.appendChild(fieldXfPosition);
        
        // field initial y position
        Element fieldYiPosition = doc.createElement("yi");
        fieldYiPosition.appendChild(doc.createTextNode(String.valueOf(field.getInitialPosition()[1])));
        rootFieldElement.appendChild(fieldYiPosition);
                
        // field final y position
        Element fieldYfPosition = doc.createElement("yf");
        fieldYfPosition.appendChild(doc.createTextNode(String.valueOf(field.getFinalPosition()[1])));
        rootFieldElement.appendChild(fieldYfPosition);
        
         // field initial z position
        Element fieldZiPosition = doc.createElement("zi");
        fieldZiPosition.appendChild(doc.createTextNode(String.valueOf(field.getInitialPosition()[2])));
        rootFieldElement.appendChild(fieldZiPosition);
                
        // field final z position
        Element fieldZfPosition = doc.createElement("zf");
        fieldZfPosition.appendChild(doc.createTextNode(String.valueOf(field.getFinalPosition()[2])));
        rootFieldElement.appendChild(fieldZfPosition);
        
        return doc;
    }
        
    private Document populateXmlSource(Source source) throws ParserConfigurationException 
    {
        // source root element
        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("source");
        doc.appendChild(rootElement);

        // source type element
        Element type = doc.createElement("type");
        type.appendChild(doc.createTextNode(String.valueOf(source.getType())));
        rootElement.appendChild(type);

        // frequency element
        Element frequency = doc.createElement("frequency");
        frequency.appendChild(doc.createTextNode(String.valueOf(source.getFrequency())));
        rootElement.appendChild(frequency);

        // frequency width element
        Element frequencyWidth = doc.createElement("frequency_width");
        frequencyWidth.appendChild(doc.createTextNode(String.valueOf(source.getWidth())));
        rootElement.appendChild(frequencyWidth);

        // start time element
        Element startTime = doc.createElement("start_time");
        startTime.appendChild(doc.createTextNode(String.valueOf(source.getStartTime())));
        rootElement.appendChild(startTime);

        // end time element
        Element endTime = doc.createElement("end_time");
        endTime.appendChild(doc.createTextNode(String.valueOf(source.getEndTime())));
        rootElement.appendChild(endTime);

        return doc;
    }
        
    private Document populateXmlComputacionalDomain(ComputacionalDomain domain) throws ParserConfigurationException
    {
        // Computacional Domain root element
        Document doc = docBuilder.newDocument();        
        Element rootElement = doc.createElement("computacional_domain");
        doc.appendChild(rootElement);

        // initial x coordinate element
        Element iCoordX = doc.createElement("xi");
        iCoordX.appendChild(doc.createTextNode(String.valueOf(domain.getCoordXInitial())));
        rootElement.appendChild(iCoordX);
        
        // final x coordinate element
        Element fCoordX = doc.createElement("xf");
        fCoordX.appendChild(doc.createTextNode(String.valueOf(domain.getCoordXFinal())));
        rootElement.appendChild(fCoordX);
        
        // initial y coordinate element
        Element iCoordY = doc.createElement("yi");
        iCoordY.appendChild(doc.createTextNode(String.valueOf(domain.getCoordYInitial())));
        rootElement.appendChild(iCoordY);
        
        // final y coordinate element
        Element fCoordY = doc.createElement("yf");
        fCoordY.appendChild(doc.createTextNode(String.valueOf(domain.getCoordYFinal())));
        rootElement.appendChild(fCoordY);
        
        // initial z coordinate element
        Element iCoordZ = doc.createElement("zi");
        iCoordZ.appendChild(doc.createTextNode(String.valueOf(domain.getCoordZInitial())));
        rootElement.appendChild(iCoordZ);
        
        // final z coordinate element
        Element fCoordZ = doc.createElement("zf");
        fCoordZ.appendChild(doc.createTextNode(String.valueOf(domain.getCoordZFinal())));
        rootElement.appendChild(fCoordZ);
        
        // x's extension element
        Element xExtension = doc.createElement("x_extension");
        xExtension.appendChild(doc.createTextNode(String.valueOf(domain.getXExtension())));
        rootElement.appendChild(xExtension);
        
        // y's extension element
        Element yExtension = doc.createElement("y_extension");
        yExtension.appendChild(doc.createTextNode(String.valueOf(domain.getYExtension())));
        rootElement.appendChild(yExtension);
        
        // z's extension element
        Element zExtension = doc.createElement("z_extension");
        zExtension.appendChild(doc.createTextNode(String.valueOf(domain.getZExtension())));
        rootElement.appendChild(zExtension);
               
        // pml thickness element
        Element pmlTickness = doc.createElement("pml_thickness");
        pmlTickness.appendChild(doc.createTextNode(String.valueOf(domain.getPmlThickness())));
        rootElement.appendChild(pmlTickness);       
         
        // resolution element
        Element resolution = doc.createElement("resolution");
        resolution.appendChild(doc.createTextNode(String.valueOf(domain.getResolution())));
        rootElement.appendChild(resolution);
                
        return doc;
    }
    
    private Document populateXmlProjectSettings(ProjectSettings projectSettings) throws ParserConfigurationException 
    {
        // project settings root element
        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("project_settings");
        doc.appendChild(rootElement);

        // project directory element
        Element projectDirectory = doc.createElement("project_directory");
        projectDirectory.appendChild(doc.createTextNode(String.valueOf(projectSettings.getProjectDirectory())));
        rootElement.appendChild(projectDirectory);

        // pre-processing flag element
        Element isPreProcessing = doc.createElement("is_pre_processing");
        isPreProcessing.appendChild(doc.createTextNode(String.valueOf(projectSettings.isPreprocessing())));
        rootElement.appendChild(isPreProcessing);

        // post-processing flag element
        Element isPostProcessing = doc.createElement("is_post_processing");
        isPostProcessing.appendChild(doc.createTextNode(String.valueOf(projectSettings.isPostProcessing())));
        rootElement.appendChild(isPostProcessing);

        // movie 2D flag element
        Element isMovie2D = doc.createElement("is_movie2d");
        isMovie2D.appendChild(doc.createTextNode(String.valueOf(projectSettings.isMovie2D())));
        rootElement.appendChild(isMovie2D);
        
        // movie 2D flag element
        Element isMovie3D = doc.createElement("is_movie3d");
        isMovie3D.appendChild(doc.createTextNode(String.valueOf(projectSettings.isMovie3D())));
        rootElement.appendChild(isMovie3D);
        
        // movie name element
        Element movieName = doc.createElement("movie_name");
        movieName.appendChild(doc.createTextNode(String.valueOf(projectSettings.getMovieName())));
        rootElement.appendChild(movieName);
        
        // movie name element
        Element hasSource = doc.createElement("has_source");
        hasSource.appendChild(doc.createTextNode(String.valueOf(projectSettings.hasSource())));
        rootElement.appendChild(hasSource);
        
        // field at fixed time flag element
        Element isFieldAtFixedTime = doc.createElement("is_fixed_field");
        isFieldAtFixedTime.appendChild(doc.createTextNode(String.valueOf(projectSettings.isFieldAtFixedTime())));
        rootElement.appendChild(isFieldAtFixedTime);
        
        // post processing axis direction element
        Element postProcessingAxisDirection = doc.createElement("axis_direction");
                
        if (projectSettings.getAxisDirection() != null)   
            postProcessingAxisDirection.appendChild(doc.createTextNode(projectSettings.getAxisDirection()));
        else
            postProcessingAxisDirection.appendChild(doc.createTextNode(""));
        
        rootElement.appendChild(postProcessingAxisDirection);
        
        // post processing file names element
        Element imageFileNames = doc.createElement("image_file_names");
               
        if (projectSettings.getPostProcessingImageFiles() != null)
        {                
            for (int i = 0; i < projectSettings.getPostProcessingImageFiles().size(); i++)
            {                
                Element currentFileName = doc.createElement(String.valueOf("file_name_" + i));                
                currentFileName.appendChild(doc.createTextNode(projectSettings.getPostProcessingImageFiles().get(i)));
                imageFileNames.appendChild(currentFileName);
            }
        }
        else
        {
            imageFileNames.appendChild(doc.createTextNode(""));
        }
                
        rootElement.appendChild(imageFileNames);
            
        // post processing field range element
        Element fieldRange = doc.createElement("field_range");
        Element initialRange = doc.createElement("initial_range");
        Element finalRange = doc.createElement("final_range");
       
        if (projectSettings.getFieldRange() != null)
        {  
            initialRange.appendChild(doc.createTextNode(String.valueOf(projectSettings.getFieldRange()[0])));
            finalRange.appendChild(doc.createTextNode(String.valueOf(projectSettings.getFieldRange()[1])));
        }
        else
        {            
            initialRange.appendChild(doc.createTextNode(""));
            finalRange.appendChild(doc.createTextNode(""));
        }
        
        fieldRange.appendChild(initialRange);
        fieldRange.appendChild(finalRange);
        rootElement.appendChild(fieldRange);
        
        return doc;
    }
}
